package jmine.tec.persist.hibernate.initializer;

import jmine.tec.persist.hibernate.proxy.SerializableLazyProxy;

import org.hibernate.collection.PersistentCollection;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;

/**
 * Enum com as diferentes formas de manipular um objeto que têm carregamento "lazy".
 * 
 * @author wellington.santos
 * @created May 30, 2012
 */
public enum LazyResolverEnum {

    /** 
     * Tratamento para a implementação de {@link SerializableLazyProxy}.
     */
    SERIALIZABLE_LAZY_PROXY() {
        
        /**
         * {@inheritDoc}
         *
         * @return {@inheritDoc}
         */
        @Override
        public boolean isUninitialized(Object object) {
            return ((SerializableLazyProxy) object).isUninitialized();
        }

        /**
         * {@inheritDoc}
         *
         * @return {@inheritDoc}
         */
        @Override
        public Object resolveIfAlreadyInitialized(Object target) {
            SerializableLazyProxy lazyProxy = (SerializableLazyProxy) target;
            if (lazyProxy.isUninitialized()) {
                return target;
            }
            return lazyProxy.getImplementation();
        }

        /**
         * {@inheritDoc}
         *
         * @return {@inheritDoc}
         */
        @Override
        public Object resolve(Object target) {
            return ((SerializableLazyProxy) target).getImplementation();
        }
        
    },

    /** 
     * Tratamento para a implementação de {@link HibernateProxy}.
     */
    HIBERNATE_PROXY() {
        
        /**
         * {@inheritDoc}
         *
         * @return {@inheritDoc}
         */
        @Override
        public boolean isUninitialized(Object object) {
            return ((HibernateProxy) object).getHibernateLazyInitializer().isUninitialized();
        }

        /**
         * {@inheritDoc}
         *
         * @return {@inheritDoc}
         */
        @Override
        public Object resolveIfAlreadyInitialized(Object target) {
            LazyInitializer lazyInitializer = ((HibernateProxy) target).getHibernateLazyInitializer();
            if (lazyInitializer.isUninitialized()) {
                return target;
            }
            return lazyInitializer.getImplementation();
        }

        /**
         * {@inheritDoc}
         *
         * @return {@inheritDoc}
         */
        @Override
        public Object resolve(Object target) {
            return ((HibernateProxy) target).getHibernateLazyInitializer().getImplementation();
        }
        
    },

    /** 
     * Tratamento para a implementação de {@link PersistentCollection}.
     */
    HIBERNATE_COLLECTION() {
        
        /**
         * {@inheritDoc}
         *
         * @return {@inheritDoc}
         */
        @Override
        public boolean isUninitialized(Object object) {
            return !((PersistentCollection) object).wasInitialized();
        }

        /**
         * {@inheritDoc}
         *
         * @return {@inheritDoc}
         */
        @Override
        public Object resolveIfAlreadyInitialized(Object target) {
            PersistentCollection collection = (PersistentCollection) target;
            if (collection.wasInitialized()) {
                collection.getValue();
            }
            return target;
        }

        /**
         * {@inheritDoc}
         *
         * @return {@inheritDoc}
         */
        @Override
        public Object resolve(Object target) {
            return ((PersistentCollection) target).getValue();
        }
        
    },

    /** 
     * Tratamento para objetos sem carregamento "lazy".
     */
    NON_LAZY_OBJECT() {
        
        /**
         * {@inheritDoc}
         *
         * @return {@inheritDoc}
         */
        @Override
        public boolean isUninitialized(Object object) {
            return false;
        }

        /**
         * {@inheritDoc}
         *
         * @return {@inheritDoc}
         */
        @Override
        public Object resolveIfAlreadyInitialized(Object object) {
            return object;
        }

        /**
         * {@inheritDoc}
         *
         * @return {@inheritDoc}
         */
        @Override
        public Object resolve(Object object) {
            return object;
        }
        
    };

    /**
     * Verifica se um objeto é lazy e não foi inicializado ainda.
     * 
     * @param object objeto a verificar
     * @return True se for "lazy" e ainda não estiver carregado
     */
    public abstract boolean isUninitialized(Object object);

    /**
     * Obtém a implementação de um objeto que está encapsulado por uma proxy lazy apenas se os dados já foram carregados.<br>
     * Não provoca inicialização do mesmo.
     * 
     * @param object objeto a desencapsular
     * @return o objeto sem proxy somente se já estava carregado, caso contrário retorna a própria proxy
     */
    public abstract Object resolveIfAlreadyInitialized(Object object);

    /**
     * Resolve a implementação de um objeto que está encapsulado por uma proxy lazy.
     * 
     * @param object objeto a desencapsular
     * @return o objeto sem proxy e já carregado
     */
    public abstract Object resolve(Object object);

    /**
     * Obtém a implementação de enum que saberá como tratar o objeto "lazy" especificado.
     * 
     * @param object objeto em questão
     * @return a implementação de enum adequada
     */
    public static LazyResolverEnum forObject(Object object) {
        if (object != null) {
            if (object instanceof HibernateProxy) {
                return HIBERNATE_PROXY;
            }
            if (object instanceof PersistentCollection) {
                return HIBERNATE_COLLECTION;
            }
            if (object instanceof SerializableLazyProxy) {
                return SERIALIZABLE_LAZY_PROXY;
            }
        }
        return NON_LAZY_OBJECT;
    }

    /**
     * Obtém a implementação de enum que saberá como tratar a classe de "lazy" especificada.
     * 
     * @param type classe do objeto
     * @return a implementação de enum adequada
     */
    public static LazyResolverEnum forClass(Class type) {
        if (type != null) {
            if (HibernateProxy.class.isAssignableFrom(type)) {
                return HIBERNATE_PROXY;
            }
            if (PersistentCollection.class.isAssignableFrom(type)) {
                return HIBERNATE_COLLECTION;
            }
            if (SerializableLazyProxy.class.isAssignableFrom(type)) {
                return SERIALIZABLE_LAZY_PROXY;
            }
        }
        return NON_LAZY_OBJECT;
    }

}
